package util;
import java.util.GregorianCalendar;

public class KeplerLaws {

	/**
	 * Kepler laws. The starting point is midnight on startDay of startMonth
	 * @param startDay from 1 to 31 (or whatever)
	 * @param startMonth from 0 to 11
	 * @param _excentricity
	 * @param _maxTilt
	 */
	public KeplerLaws(int startDay, int startMonth, double _excentricity, double _maxTilt) {
	
		excentricity = _excentricity;
		maxTilt = _maxTilt;
		perihelionDistance = semiMajorAxis*(1-excentricity);
		sunAngle = 0;
		
		// Calculate minutesSincePerihelion and minutesSinceMaxMinTilt
		int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
		
		// First take care of perihelion. Perihelion is Jan 3rd
		if(startMonth == 0) {
			// Start date is january
			if(startDay == 3) {
				minutesSincePerihelion = 0;
			} else if(startDay < 3) {
				minutesSincePerihelion = 365*24*60 - (3-startDay)*24*60;
			} else {
				minutesSincePerihelion = (startDay - 3)*24*60;
			}
		} else {
			// Later month
			minutesSincePerihelion = (31 - 2)*24*60; // For jan
			for(int i=1; i < startMonth; i++) {
				minutesSincePerihelion += (daysInMonth[i])*24*60;
			}
			minutesSincePerihelion += (startDay - 1)*24*60;
		}
		
		// Now take care of the minutesSinceMaxMinTilt
		if(startMonth == 11) {
			// This is december, check the day
			if(startDay >= 21) {
				// We are in the min->max period
				startTilt = -maxTilt;
				endTilt = maxTilt;
				minutesSinceMaxMinTilt = (startDay - 21)*24*60;
				minutesInTiltCycle = 183*24*60;
			} else {
				// We are in the max->min period
				startTilt = maxTilt;
				endTilt = -maxTilt;
				minutesSinceMaxMinTilt = (162 + startDay-1)*24*60;
				minutesInTiltCycle = 182*24*60;
			}
		} else if(startMonth == 5) {
			// This is June, check the day
			if(startDay >= 21) {
				// We are in the max->min period
				startTilt = maxTilt;
				endTilt = -maxTilt;
				minutesSinceMaxMinTilt = (startDay - 21)*24*60;
				minutesInTiltCycle = 182*24*60;
			} else {
				// We are in the min->max period
				startTilt = -maxTilt;
				endTilt = maxTilt;
				minutesSinceMaxMinTilt = (163 + startDay - 1)*24*60;
				minutesInTiltCycle = 183*24*60;
			}
		} else if(startMonth <= 4) {
			// We are in the min->max period
			startTilt = -maxTilt;
			endTilt = maxTilt;
			minutesSinceMaxMinTilt = 11*24*60;
			for(int i=0; i < startMonth; i++)
				minutesSinceMaxMinTilt += daysInMonth[i]*24*60;
			minutesSinceMaxMinTilt += (startDay -1)*24*60;
			minutesInTiltCycle = 183*24*60;
		} else {
			// We are in the max->min period
			startTilt = maxTilt;
			endTilt = -maxTilt;
			minutesSinceMaxMinTilt = 9*24*60;
			for(int i=6; i < startMonth; i++) {
				minutesSinceMaxMinTilt += daysInMonth[i]*24*60;
			}
			minutesSinceMaxMinTilt += (startDay - 1)*24*60;
			minutesInTiltCycle = 182*24*60;
			
		}
		step(0); // Calculate all variables
		
	}
	
	public void step(int minutes) {
		
		final double radiansPerMin = 2*Math.PI/(24*60);
		
        minutesSinceMaxMinTilt += minutes;
        minutesSincePerihelion += minutes;
        
        /* Correct minutesSince variables */
        while(minutesSinceMaxMinTilt >= minutesInTiltCycle) {
        	minutesSinceMaxMinTilt -= minutesInTiltCycle;
        	if(startTilt == maxTilt) {
        		endTilt = maxTilt;
        		startTilt = -maxTilt;
        		minutesInTiltCycle = 183*24*60;
        	} else if(startTilt == -maxTilt) {
        		startTilt = maxTilt;
        		endTilt = -maxTilt;
        		minutesInTiltCycle = 182*24*60;
        	}
        }
        while(minutesSincePerihelion >= 525600)
        	minutesSincePerihelion -= 525600;
		
        /* Calculate current tilt */
        currentTilt = startTilt + (double)minutesSinceMaxMinTilt/(double)minutesInTiltCycle*(float)(endTilt - startTilt);
        
        /* Calculate the distance */
        double tempM = 2*Math.PI*((double)minutesSincePerihelion/(double)525600);
        double tempE = tempM + excentricity*Math.sin(tempM);
        for(int i=0; i < 3; i++) {
        	// Three iterations for now
        	tempE = tempM + excentricity*Math.sin(tempE);
        }
        distance = semiMajorAxis*(1-excentricity*Math.cos(tempE));
        
        trueAnomaly = Math.acos((Math.cos(tempE) - excentricity)/(1 - excentricity*Math.cos(tempE)));
        
        sunAngle += radiansPerMin*minutes; 
		while(sunAngle >= 2*Math.PI)
			sunAngle -= 2*Math.PI;
        
	}
	
	public double getCurrentTilt() {
		return currentTilt;
	}

	public double getDistance() {
		return distance;
	}
	
	public double getPerihelionDistance() {
		return perihelionDistance;
	}
	
	public double getSunAngle() {
		return sunAngle;
	}
	
	public double getRealAnomaly() {
		return trueAnomaly;
	}
	
	protected long minutesSincePerihelion; // Time since last perihelion
	protected long minutesSinceMaxMinTilt; // Time since last time we reached max or min tilt
	protected long minutesInTiltCycle;
	
	protected double excentricity;
	protected final double semiMajorAxis = 149.6;
	protected double perihelionDistance;
	protected double distance;
    protected double currentTilt; // Tilt in radians
    protected double maxTilt; // Maximum tilt in radians
    protected double startTilt; // Tilt to use as starting point
    protected double endTilt;
    protected double sunAngle;
    protected double trueAnomaly;


}
